#!/usr/bin/env python3
"""
Remote Control System - Playback Side
Receives events via UDP and simulates them using pyautogui
"""

import json
import socket
import time
import threading
import sys
import pyautogui
from colorama import init, Fore, Style

# Initialize colorama for colored output
init()

# Configure pyautogui
pyautogui.FAILSAFE = True  # Move mouse to top-left corner to abort
pyautogui.PAUSE = 0.01     # Small pause between actions

class InputPlayback:
    def __init__(self, listen_port=3001):
        self.listen_port = listen_port
        self.is_playing = False
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.event_count = 0
        self.pressed_keys = set()  # Track pressed keys
        
        print(f"{Fore.CYAN}🎮 Input Playback initialized - Listening on port: {listen_port}{Style.RESET_ALL}")
    
    def start(self):
        """Start listening for events and playing them back"""
        if self.is_playing:
            print(f"{Fore.YELLOW}⚠️  Already playing back input{Style.RESET_ALL}")
            return
        
        self.is_playing = True
        print(f"{Fore.GREEN}🚀 Starting input playback...{Style.RESET_ALL}")
        
        try:
            self.socket.bind(('0.0.0.0', self.listen_port))
            print(f"{Fore.GREEN}✅ UDP server listening on 0.0.0.0:{self.listen_port}{Style.RESET_ALL}")
            print(f"{Fore.BLUE}🎯 Ready to receive and simulate input events...{Style.RESET_ALL}")
            print(f"{Fore.BLUE}⏹️  Press Ctrl+C to stop{Style.RESET_ALL}")
            
            # Start statistics thread
            self.stats_thread = threading.Thread(target=self.show_stats, daemon=True)
            self.stats_thread.start()
            
            # Main event loop
            while self.is_playing:
                try:
                    data, addr = self.socket.recvfrom(1024)
                    event_data = json.loads(data.decode('utf-8'))
                    self.handle_event(event_data)
                    self.event_count += 1
                    
                except socket.timeout:
                    continue
                except json.JSONDecodeError as e:
                    print(f"{Fore.RED}❌ Error parsing event: {e}{Style.RESET_ALL}")
                except Exception as e:
                    if self.is_playing:  # Only show error if we're still supposed to be playing
                        print(f"{Fore.RED}❌ Error receiving event: {e}{Style.RESET_ALL}")
                        
        except Exception as e:
            print(f"{Fore.RED}❌ Server error: {e}{Style.RESET_ALL}")
            self.stop()
    
    def stop(self):
        """Stop playback"""
        if not self.is_playing:
            print(f"{Fore.YELLOW}⚠️  Not currently playing back{Style.RESET_ALL}")
            return
        
        self.is_playing = False
        self.socket.close()
        print(f"{Fore.RED}🛑 Input playback stopped{Style.RESET_ALL}")
        print(f"{Fore.CYAN}📈 Total events processed: {self.event_count}{Style.RESET_ALL}")
    
    def handle_event(self, event_data):
        """Handle incoming events and simulate them"""
        if not self.is_playing:
            return
        
        try:
            event_type = event_data.get('type')
            
            if event_type == 'mousemove':
                x, y = event_data.get('x', 0), event_data.get('y', 0)
                pyautogui.moveTo(x, y)
                
            elif event_type == 'mousedown':
                x, y = event_data.get('x', 0), event_data.get('y', 0)
                button = event_data.get('button', 'left')
                pyautogui.mouseDown(x, y, button=button)
                
            elif event_type == 'mouseup':
                x, y = event_data.get('x', 0), event_data.get('y', 0)
                button = event_data.get('button', 'left')
                pyautogui.mouseUp(x, y, button=button)
                
            elif event_type == 'mousewheel':
                x, y = event_data.get('x', 0), event_data.get('y', 0)
                dy = event_data.get('dy', 0)
                # Move to position and scroll
                pyautogui.moveTo(x, y)
                if dy > 0:
                    pyautogui.scroll(1)  # Scroll up
                elif dy < 0:
                    pyautogui.scroll(-1)  # Scroll down
                    
            elif event_type == 'keydown':
                key = self.convert_key(event_data.get('key', ''))
                if key and key not in self.pressed_keys:
                    self.pressed_keys.add(key)
                    pyautogui.keyDown(key)
                    
            elif event_type == 'keyup':
                key = self.convert_key(event_data.get('key', ''))
                if key and key in self.pressed_keys:
                    self.pressed_keys.remove(key)
                    pyautogui.keyUp(key)
            
            else:
                print(f"{Fore.YELLOW}⚠️  Unknown event type: {event_type}{Style.RESET_ALL}")
                
        except Exception as e:
            print(f"{Fore.RED}❌ Error handling {event_data.get('type', 'unknown')} event: {e}{Style.RESET_ALL}")
    
    def convert_key(self, key_name):
        """Convert key names from pynput to pyautogui format"""
        if not key_name:
            return None
            
        # Handle special keys
        key_mapping = {
            'Key.space': 'space',
            'Key.enter': 'enter',
            'Key.tab': 'tab',
            'Key.backspace': 'backspace',
            'Key.delete': 'delete',
            'Key.esc': 'escape',
            'Key.shift': 'shift',
            'Key.shift_l': 'shiftleft',
            'Key.shift_r': 'shiftright',
            'Key.ctrl': 'ctrl',
            'Key.ctrl_l': 'ctrlleft',
            'Key.ctrl_r': 'ctrlright',
            'Key.alt': 'alt',
            'Key.alt_l': 'altleft',
            'Key.alt_r': 'altright',
            'Key.cmd': 'winleft',
            'Key.cmd_l': 'winleft',
            'Key.cmd_r': 'winright',
            'Key.up': 'up',
            'Key.down': 'down',
            'Key.left': 'left',
            'Key.right': 'right',
            'Key.home': 'home',
            'Key.end': 'end',
            'Key.page_up': 'pageup',
            'Key.page_down': 'pagedown',
            'Key.caps_lock': 'capslock',
            'Key.num_lock': 'numlock',
            'Key.scroll_lock': 'scrolllock',
            'Key.insert': 'insert',
            'Key.pause': 'pause',
            'Key.print_screen': 'printscreen',
        }
        
        # Add function keys
        for i in range(1, 13):
            key_mapping[f'Key.f{i}'] = f'f{i}'
        
        # Check if it's a special key
        if key_name in key_mapping:
            return key_mapping[key_name]
        
        # Handle regular characters
        if len(key_name) == 1:
            return key_name.lower()
        
        # If we can't convert it, return None
        return None
    
    def show_stats(self):
        """Show statistics every 10 seconds"""
        while self.is_playing:
            time.sleep(10)
            if self.is_playing:
                print(f"{Fore.CYAN}📊 Events processed: {self.event_count}{Style.RESET_ALL}")

def main():
    """Main function for CLI usage"""
    import argparse
    
    parser = argparse.ArgumentParser(description='Remote Control - Input Playback')
    parser.add_argument('port', nargs='?', type=int, default=3001, help='Listen port (default: 3001)')
    
    args = parser.parse_args()
    
    print(f"{Fore.MAGENTA}🎮 Remote Control - Input Playback{Style.RESET_ALL}")
    print(f"{Fore.MAGENTA}==================================={Style.RESET_ALL}")
    print()
    
    playback = InputPlayback(args.port)
    
    try:
        playback.start()
        
    except KeyboardInterrupt:
        print(f"\n{Fore.YELLOW}🔄 Shutting down playback...{Style.RESET_ALL}")
        playback.stop()
    except Exception as e:
        print(f"{Fore.RED}💥 Error: {e}{Style.RESET_ALL}")
        playback.stop()

if __name__ == '__main__':
    main()